home *** CD-ROM | disk | FTP | other *** search
Wrap
/****************************************************************************** MODUL $Id: alias.c $ DESCRIPTION macros for XDME NOTES a part of that module is in command.c BUGS <none known> TODO -/- EXAMPLES -/- SEE ALSO command.c AUTHOR Bernd "0" Noll (b_noll@informatik.uni-kl.de) HISTORY $Log: alias.c $ 21-09-94 b_noll added DEFCMD/DEFHELP-support 24-09-94 b_noll usage of std_-read/write-file 01-10-94 b_noll introduced DEFMESSAGE 16-11-94 b_noll added GetMVBase 22-11-94 b_noll removed getmacroarg - args are normal vars now 23-11-94 b_noll removed getmacrovar - done from vars.c now 04-12-94 b_noll removed some dead protos ******************************************************************************/ /* ** (C)Copyright 1992 by Bernd Noll for null/zero-soft ** All Rights Reserved ** ** RCS Header: $Id: Alias.c 1.2 1994/09/20 11:05:32 b_noll Exp b_noll $ ** ** compiling with ** "#define PATCH_NULL" ** "#define PATCH_MACROS" ** causes that module to be used ** *!********************************************************************* *! *! MACROS - OVERVIEW ** ** this option allows translation of alias.c ** and enables a change in do_command() to allow macro-execution ** and (perhaps) one in main() to initialize the macro-lists *! *! the module supports several commands: *! SETMACRO definition of a new macro *! UNSETMACRO deletion of a macro *! *! SETMACROVAR definition of a macro's local variable *! UNSETMACROVAR deletion of suh a variable *! (at the end of a macrocall all its local variables *! are deleted automatically) *! **********************************************************************/ /************************************** Includes **************************************/ #define MACRO struct MacroNode /* OPAQUE 4 other modules */ #include "defs.h" #include "AVL.h" DEFDEFINITION( PATCH_ALIAS 1 ) /* ngra - the straight avl module is broken ... */ int iAVL_Append (APTR x, APTR y, APTR z); int iAVL_Remove (APTR x, APTR y, APTR z); int iAVL_Find (APTR x, APTR y, APTR z); #define ANY_Find(t,n) iAVL_Find (t,(APTR)NodeStringComparison, n) #define ANY_Add(t,n) iAVL_Append(t,(APTR)NodeNodeComparison, n) #define ANY_Remove(t,n) iAVL_Remove(t,(APTR)NodeNodeComparison, n) #define ANY_Init(t) *(t) = NULL; /************************************** Interne Defines & Struktures **************************************/ /* ** U might use MACRO like an opaque type */ struct MacroNode { struct Node Node; /* includes a ptr to the macro-name */ char* Body; /* statements to be executed at a macro-call (this all must fit in one line) */ char NArgs; /* 0..7 number of arguments to be given from do_command */ //short NFlags; /* PERHAPS FUTURE: 0..n number of local-flags to be supported */ //char freeme; /* NOT USED ANY MORE 0/1 flag if the macro is builtin or user-defined */ char killme; /* 0/1 flag if he maxro shall be deleted after termination of its last macrocall */ char NCalls; /* it seems to be necessary to forbid all deletions of an active macro */ }; struct MacroArg { struct MinNode ma_Node; /* hook for recursion-stack */ struct MacroNode*ma_Macro; /* a reference to the called macro w/ all its data */ VarRoot ma_Locals; /* AVL-Tree or List of Local Variables */ /* we could also have a ptr to an array of ptrs instead of a list, but then it might be necessary to use one more parameter for SETMACRO */ //char* ma_Arguments[2];/* NOT USED ANY MORE array of ptr to string; there must always be at least two entries: a 1st one referencing the macroname and a last one == NULL */ }; /************************************** Internal Variables **************************************/ /* ** *argstack contains all data which is created during one macro-call ** *delenda is a delete-buffer for all macros, will have been removed ** from their lists, but cannot deleted, as there is a prior call to ** not returned; they are deleted then aftermacro-termination or ** in exit_macros. */ static struct TreeNode* macros = NULL; /* ... OF MACRO */ static struct TreeNode* delenda= NULL; /* ... OF MACRO */ static MLIST argstack; /* ... OF struct MacroArg */ char MacroBreak = 0; /* internal possibility to terminate a macro */ /* PATCH_NULL [08 Mar 1993] : removed "static" */ /********************************************** ** ** Definition and deletion of Macros ** **********************************************/ /* ** FreeMacro() ** DelMacro() ** 2 help-Functions of do_unsetmacro (and exit_macros) ** ** DelMacro() should delete only Macros, which are not in use ** for that reason it checks usecount, and if its not 0, the ** macro is put into another list and marked to be killed by ** callmacro() after execution. */ static void FreeMacro(MACRO *m) { //if (m->freeme) { /* if that macro is not a builtin one, */ free(m->Node.ln_Name); /* free it and its contents */ free(m->Body); free(m); //} /* if */ } /* FreeMacro */ static void DelMacro (char *name) { MACRO *m; if ((m = getmacro(name))) { /* find the node representing the macro ... */ ANY_Remove (¯os, (APTR)m); /* , remove it from its tree ... */ if (!m->NCalls) { /* and if it is not in use */ FreeMacro(m); /* ... free its contents */ } else { /* ... else put it into a certain kill-list */ m->killme++; /* ... and mark it to be killed */ ANY_Add (&delenda, (APTR)m); } /* if */ } /* if */ } /* DelMacro */ /* ** SetMacro() ** a help-Function of do_setmacro (and maybe init_macros) ** (it is also called by packages.mergpack) ** which creates a new macro-node and fills copies of the parameters ** */ static int SetMacro(char *name, char *body, int nargs) { MACRO *m; if (nargs >= NARGS) { DEFMESSAGE( _MACRO_too_many_arguments, "too many arguments specified for macro" ) error (_MACRO_too_many_arguments); return(0); } /* if too many args */ DelMacro(name); /* for sure: remove all macros of the same name */ if ((m = malloc(sizeof(MACRO)))) { /* allocate a macro-structure... */ setmem(m, sizeof(MACRO),0); if ((m->Node.ln_Name = strdup(name))) { /* ... the name ... */ if ((m->Body = strdup(body))) { /* ... and the body, */ m->NArgs = nargs; /* fill in all values, */ //m->freeme = 1; ANY_Add (¯os, (APTR)m); /* and append the structure to a certain macro-list */ return 1; } /* if block copied */ free(m->Node.ln_Name); /* if anything went wrong */ } /* if name copied */ /* free all allocated frags */ free(m); /* and set an error */ } /* if struct allocated */ nomemory(); return 0; } /* SetMacro */ /********************************************** ** ** Access to Macros' arguments and local variables ** **********************************************/ DEFVARTREE( 50, "MVAR_", VAR_MV, 5, 5, VF_COP|VF_UD_FUNC, GetMVBase, NULL, SetVarIntoTree, GetVarFromTree ) Prototype VBASE *GetMVBase (void); VBASE *GetMVBase (void) { struct MacroArg* n; if (!(n = GetHead(&argstack))) return NULL; return &n->ma_Locals; } /* GetMVBase */ #if 0 /* ** SetMacroVar() ** a help-function of do_setmacrovar() */ void SetMacroVar(char *name, char *value) { VBASE* v; if (!(v = GetMVBase())) /* this function can not work if not called from within a macrocall */ SET_ABORTION( 1 ); else SetVarIntoTree (v, name, value); } /* SetMacroVar */ /* ** getmacrovar() ** get a local variable of the current(== last) macrocall ** we are supposing that only one process has access to resources ** that function is meant to be interface for getvar() */ UBYTE *getmacrovar(UBYTE *name) { return GetVarFromTree (GetMVBase(), name); } /* getmacrovar */ /* !!!this function is obsolete, since we handle macroargs like macrovars!!!!!!! ** getmacroarg() ** get an parameter of the current(== last) macrocall ** we are supposing that only one process has access to resources ** so we only have to check the topmost element of argstack *! NOTE: ARGUMENTS to a macro are READ-ONLY, *! You can't alter their values from the body of the called macro *! */ UBYTE *getmacroarg(UBYTE *name) { struct MacroArg* n; int num; UBYTE *res; if (!(n = GetHead(&argstack))) /* this function can not work if not called from within a macrocall */ return NULL; if ((strncmp(name,"arg",3) != 0) || (!is_number(name+3))) { /* and it responds only to names of the type "ARGx" with x in /N */ return(NULL); } /* if */ num = atoi(name+3); if (n->ma_Macro->NArgs>=num) { /* if the last called function has an arg ARGx */ if ((res = strdup (n->ma_Arguments[num]))) { /* and U can duplicate it, then return the copy */ return(res); } else { nomemory(); abort(NULL); } /* if (not) malloced res */ } else { abort(NULL); } /* if (not) exists argX */ } /* getmacroarg */ /* ** That function might be used as an interface for getvar ** to access the contents of a macro */ char *GetFuncBody(char *name) { MACRO* m = NULL; char* str = NULL; if (m = (MACRO*)getmacro(name)) { str = strdup(m->Body); } /* if */ return(str); } /* GetFuncBody */ #endif /********************************************** ** ** Usage of Macros ** ** for calls from do_command the sequence must ** be like that: ** ** if (m=getmacro) then ** for i=1 to nummacroargs(m) do ** put nextarg to av[i] ** od; ** callmacro(m); ** fi; ** ** callmacro() pushes av[i] onto argstack and ** clears them, then locks the macro and ** calls do_command with a copy of the ** macro's block ** when the execution returns, av[i] are ** restored and top of argstack is cleared ** **********************************************/ /* ** callmacro() ** Call a Macro as interface from do_command() ** (first you must have got a "handle" via getmacro()) */ Prototype int callmacro (MACRO*); int callmacro (MACRO *m) { struct MacroArg *n; //char** args; int ret = 0; int i; int len = sizeof(struct MacroArg); n = (struct MacroArg*)malloc(len); /* allocate space for the stack-element... */ if (n) { setmem(n,len,0); /* clear all data */ AddHead((LIST*)&argstack,(NODE*)n); /* and push the struct to argstack */ InitVars (&n->ma_Locals); /* for use with encapsulated local variables we must initialize their list */ av[0] = m->Node.ln_Name; /* define argslot 0 - it might be asked during execution, and perhaps it was not set in do_command */ for (i = m->NArgs; i >= 0; i--) { char nam[6] = "arg0"; nam[3] += i; SetVarIntoTree (&n->ma_Locals, nam, av[i]);/* then put all arguments into the struct */ //av[i] = NULL; /* and clear their global values */ } /* for */ //AVL_ScanTree (&n->ma_Locals, PrintNode, 1); n->ma_Macro = m; m->NCalls++; /* disable deletion of that macro during execution */ MacroBreak = 0; /* clear the return - flag */ ret = buffered_do_command(m->Body); /* and call the macrobody */ if (MacroBreak) { ret = 1-ret; SET_ABORTION( MacroBreak == 2 ); MacroBreak = 0; } /* if */ //for (i = m->NArgs; i >= 0; i--) { /* after the call restore the global values of all args, ... */ // av[i] = args[i]; //} /* for */ m->NCalls--; /* and enable deletion of that macro after execution */ if (m->NCalls == 0 && m->killme) { /* if we must delete the macro, do it */ ANY_Remove(&delenda, (APTR)m); FreeMacro(m); } /* if */ DelAllVarsFromTree (&n->ma_Locals); /* delete all local variables ... */ Remove((NODE*)n); /* and pop the margs-struct from argstack */ } else nomemory(); if (n) /* and free anything you allocated */ free(n); return ret; } /* callmacro */ /* ** getmacro() ** ** Find a Function of a given Name ** so macros can be tested for existance and user can get a ptr to a "Handle" ** that function is part of the interface to do_command() */ Prototype MACRO *getmacro (char *name); MACRO *getmacro(char *name) { MACRO *m; m = (MACRO *)ANY_Find (¯os, name); return m; } /* getmacro */ /* ** nummacroargs() ** Get the Number of Arguments of a Macro ** that function is used by do_command to get informations ** how many argslots to fill */ Prototype int nummacroargs (MACRO *); int nummacroargs (MACRO *m) { return m? (m->NArgs): 0; } /* nummacroargs */ /********************************************** ** ** Initial Access to Macros ** **********************************************/ #if 0 /* support of default macros has been dropped, since there is imho no really */ /* need for it */ /* ** resmacros ** that structure shows all builtin macros ** which are defined at program start ** they are initialized via GetDefMacros */ typedef struct _mres { char * name; char * body; int numargs; } MRES; static CONST MRES resmacros[] = { /* name, block, args */ { "alias", "setmacro $arg1 0 $arg2", 2 }, { "aslload", "arpload", 0 }, { "aslinsfile", "arpinsfile", 0 }, { "aslfont", "arpfont", 0 }, { "aslsave", "arpsave", 0 }, /* { "asl", "arp", 0 }, */ { "unalias", "unsetmacro $arg1", 1 }, { "firstwindow", "select f", 0 }, { "lastwindow", "select l", 0 }, { "nextwindow", "select n", 0 }, { "prevwindow", "select p", 0 }, { "so", "if m saveold", 0 }, { NULL, NULL, 0 } }; /* resmacros */ /* ** GetDefMacros ** initialize the builtin default-macros */ static int GetDefMacros (void) { int i; for (i = 0; resmacros[i].name; i++) { if (!SetMacro(resmacros[i].name, resmacros[i].body, resmacros[i].numargs)) { return (0); } /* if error */ } /* for all builtins */ return (1); } /* GetDefMacros */ #else #define GetDefMacros() #endif /* ** ALIAS_init () ** ** Init All Macro-Stuff ** init all name-lists ** init the arg-stack ** enable the builtin macros (if macros are global) ** ** That function has to be called at programstart */ DEFAUTOINIT( ALIAS_Init ) { NewList ((LIST*)&argstack); ANY_Init (&delenda); ANY_Init (¯os); GetDefMacros (); } /* init_macros */ /********************************************** ** ** IO of Macros (dumping) ** **********************************************/ /* That static variable is needed to communicate between savesinglemacro and savemacros */ static FILE * mfi; static void savesinglemacro (MACRO *m) { if (m && mfi) { fprintf (mfi, "\tMACRO %s\n", m->Node.ln_Name); if (m->NArgs) { fprintf (mfi, "\t ARGS %d\n", m->NArgs); } /* if */ fprintf (mfi, "\t BODY %s\n", m->Body); } /* if */ } /* savesinglemacro */ static int ALIAS_save (FILE *fo) { fprintf(fo, "MACROS START\n"); mfi = fo; AVL_ScanTree (¯os, (APTR)savesinglemacro, 1); fprintf(fo, "MACROS END\n"); return (1); } /* savemacros */ static int ALIAS_load (FILE *fi, int *lineno) { char *buf; char nmacro [128]; char nargs [128]; char body [2*LINE_LENGTH]; buf = getnextcomline(fi, lineno); if (!buf) { abort(0); } /* if */ if (strncmp(buf, "MACROS START", 12) != 0) { DEFMESSAGE( _MACRO_read_no_header, "No Macrostart header" ) error (_MACRO_read_no_header); abort(0); } /* if */ nmacro[0] = 0; nargs [0] = 0; body [0] = 0; while (!IS_ABORTED()) { buf = getnextcomline(fi, lineno); if (!buf) { abort(0); } /* if */ if (strncmp(buf, "MACROS END", 10) == 0) { return(1); } else if (strncmp(buf, "MACRO ", 6) == 0) { buf += 6; strncpy(nmacro, buf, 128); } else if (strncmp(buf, "ARGS ", 5) == 0) { buf += 5; if (nargs[0] != 0) { DEFMESSAGE( _MACRO_read_numargs_wo_name, "%s:\nDeclared numargs not w/in a Macro" ) error(_MACRO_read_numargs_wo_name, av[0]); abort(0); } /* if */ strncpy(nargs, buf, 128); } else if (strncmp(buf, "BEGIN", 5) == 0) { int len = 0; buf += 5; while (*buf && *buf<33) buf++; if (nmacro[0] == 0) { DEFMESSAGE( _MACRO_read_body_wo_name, "<%s:\nDeclared Body w/out Macro" ) error(_MACRO_read_body_wo_name, av[0]); abort(0); } /* if */ if (*buf) { len = strlen(buf); strcpy (body, buf); } /* if */ while ((!IS_ABORTED()) && (buf = getnextcomline(fi, lineno))) { char * inter = &body[len]; if (strncmp (buf, "END", 3) == 0) { goto setit; } /* if */ len += strlen(buf)+1; if (len < 2*LINE_LENGTH) { *inter = ' '; strcpy(inter+1, buf); } else { DEFMESSAGE( _MACRO_read_body_too_big, "%s:\nmacro-body too long" ) error (_MACRO_read_body_too_big, av[0]); } /* if */ } /* while */ goto retfail; } else if (strncmp(buf, "BODY", 4) == 0 && (buf[4] < 0x21)) { buf += 4; if (*buf) buf++; if (nmacro[0] == 0) { DEFMESSAGE( _MACRO_read_body_wo_macro, "<%s:\nDeclared Body w/out Macro" ) error(_MACRO_read_body_wo_macro, av[0]); abort(0); } /* if */ strncpy(body, buf, 2*LINE_LENGTH); setit: av[1] = nmacro; av[2] = nargs[0] ? nargs : "0"; av[3] = body; do_setmacro(); nmacro[0] = 0; nargs [0] = 0; } else { DEFMESSAGE( _MACRO_read_unknown_id, "%s:\nunknown identifier '%s'" ) error(_MACRO_read_unknown_id, av[0], buf); } /* if types */ } /* while not ready */ retfail: return(0); } /* loadmacros */ /*************************************************** XDME COMMAND INTERFACE ***************************************************/ /* *! >SETMACROVAR name value *! >SMV name value *! define a local variable for the current macro-call *! all local vars will be deleted at the end of a macro-execution *! (SMV is a shortcut for SETMACROVAR as we can't define a short *! macro for that command) *! */ /*DEFHELP #cmd macro,var SMV name value - short for @{B}SETMACROVAR@{UB} */ /*DEFHELP #cmd macro,var SETMACROVAR name value - create/modify a macrolocal variable inside a macro */ DEFUSERCMD("SMV", 2, CF_VWM|CF_ICO|CF_COK, void, do_setmacrovar, (void),;) DEFUSERCMD("SetMacroVar", 2, CF_VWM|CF_ICO|CF_COK, void, do_setmacrovar, (void),) { VBASE *v; if (!(v = GetMVBase())) /* this function can not work if not called from within a macrocall */ SET_ABORTION( 1 ); else SetVarIntoTree (v, av[1], av[2]); } /* do_setmacrovar */ /* *! >UNSETMACROVAR name *! drop a local variable before the end of the current macro-call *! all local vars will be deleted at the end of a macro-execution *! so that command may be obsolete *! */ /*DEFHELP #cmd macro,var UNSETMACROVAR name - deletion of a macro's local variable */ DEFUSERCMD("UnsetMacroVar", 1, CF_VWM|CF_ICO|CF_COK, void, do_unsetmacrovar, (void),) { VBASE *v; if (!(v = GetMVBase())) /* this function can not work if not called from within a macrocall */ SET_ABORTION( 1 ); else DelVarFromTree (v, av[1]); } /* do_unsetmacrovar */ /* *! >RET *! Terminate the current macro *! */ /*DEFHELP #cmd macro RET - terminate a macro (before reaching its end) */ DEFUSERCMD("Ret", 0, CF_VWM|CF_ICO|CF_COK, void, do_retmacro, (void),) { MacroBreak = IS_ABORTED() ? 2 : 1; SET_ABORTION(1); } /* do_retmacro */ /* *! >SETMACRO macroname numargs body *! create a macro definition *! please note that a macro-body is read twice once on definition *! and once on execution (variables should be excaped so) *! *! after such a definition it is possible to use macroname like *! any XDME-command; the following numargs expressions are used *! as the macros variables, they can be accessed for read with *! $arg0-$arg<numargs> (not for write)($arg0 returns the macro's *! name) *! *! BUG: if numargs is not a number then this command *! will assume a value of "0" */ /*DEFHELP #cmd macro SETMACRO name nargs body - create/modify the commandmacro @{B}name@{UB} with @{B}nargs@{UB} arguments */ DEFUSERCMD("SetMacro", 3, CF_VWM|CF_ICO|CF_COK, void, do_setmacro, (void),) { SetMacro (av[1], av[3], atoi(av[2])); } /* do_setmacro */ /* *! >UNSETMACRO name *! remove a macro definition *! */ /*DEFHELP #cmd macro UNSETMACRO name - delete the commandmacro @{B}name@{UB} */ DEFUSERCMD("UnsetMacro", 1, CF_VWM|CF_ICO|CF_COK, void, do_unsetmacro, (void),) { DelMacro(av[1]); } /* do_unsetmacro */ /* *! >MACROSAVE filename *! save the current macros into a file of a special format *! */ /*DEFHELP #cmd macro MACROSAVE filename - save all commandmacros into a file with a special format */ DEFUSERCMD("MacroSave", 1, CF_VWM|CF_ICO, void, do_macrosave, (void),) { std_writefile ((int (*) (FILE *, APTR))ALIAS_save, NULL); } /* do_macrosave */ /* *! >MACROLOAD filename *! read a special macrofile *! */ /*DEFHELP #cmd macro MACROLOAD name - load commandmacros from a file */ DEFUSERCMD("MacroLoad", 1, CF_VWM|CF_ICO, void, do_macroload, (void),) { int lineno = 0; std_readfile (ALIAS_load, &lineno); } /* do_macrosave */ /****************************************************************************** ***** END macros.c ******************************************************************************/ /* void PrintNode (struct Node *n) { printf ("PN: %08lx/`%s'\n", n, n->ln_Name); } */